package ms.jasim.model.editor.views;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.RectangularShape;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import ms.jasim.framework.IJasimAppContext;
import ms.jasim.framework.SolutionSimulator;
import ms.jasim.framework.SolutionSimulator.Agent;
import ms.jasim.framework.SolutionSimulator.HistoryEntry;
import ms.jasim.framework.SolutionSimulator.SimulatedAction;
import ms.jasim.model.JasimModel;
import ms.jasim.pddl.PddlSolution;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.SymbolAxis;
import org.jfree.chart.axis.TickUnitSource;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.labels.XYItemLabelGenerator;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.general.Dataset;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYIntervalSeries;
import org.jfree.data.xy.XYIntervalSeriesCollection;
import org.jfree.experimental.chart.swt.ChartComposite;
import org.jfree.ui.GradientPaintTransformer;
import org.jfree.ui.RectangleEdge;

import swing2swt.layout.BorderLayout;

public class SimulationTimelinePanel {


	private static final long serialVersionUID = 1L;
	//private ChartPanel chartPanel;
	//private JTextArea txtTimeline;
	private Composite container;
	private ChartComposite chartFrame;
	private MyXYItemLabelGenerator labelGenerator;

	public static class XYItemLabelItem {
		private int series, item;
		private SimulatedAction label;

		public XYItemLabelItem(int seriesIdx, int i, SimulatedAction action) {
			series = seriesIdx;
			item = i;
			label = action;
			// System.out.printf("Action=%s, series=%d, item=%d\r\n", action.toString(), seriesIdx, i);
		}

		public int getSeries() {
			return series;
		}

		public void setSeries(int series) {
			this.series = series;
		}

		public int getItem() {
			return item;
		}

		public void setItem(int item) {
			this.item = item;
		}

		public String getLabel() {
			switch(label.getAssociateAction().getFunctor()) {
			case AND_DECOMPOSES2:
			case AND_DECOMPOSES3:
				return "D";
			case PASSES:
				return "P";
			case SATISFIES:
				return "S:" + label.getGoal().getName();
			}
			return "";
		}
		
		public SimulatedAction getAction() {
			return label;
		}
	
		public void setLabel(String label) {
			
		}
	}

	public static class MyXYItemLabelGenerator implements XYItemLabelGenerator {

		public final List<XYItemLabelItem> items = new ArrayList<XYItemLabelItem>();

		@Override
		public String generateLabel(XYDataset arg0, int arg1, int arg2) {
			for (XYItemLabelItem item : items)
				if (item.series == arg1 && item.item == arg2)
					return item.getLabel();
			return null;
		}
		
		public SimulatedAction getAction(int series, int itemidx) {
			for (XYItemLabelItem item : items)
				if (item.series == series && item.item == itemidx)
					return item.getAction();
			return null;
		}
	}
	
	class MyBarPainter extends StandardXYBarPainter {
		private static final long serialVersionUID = 1L;
		
		@Override
		public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row,
				int column, RectangularShape bar, RectangleEdge base) {
			Double arcsize = bar.getHeight()/1.5;
			bar = new RoundRectangle2D.Double(bar.getX(), bar.getY(), bar.getWidth(), bar.getHeight(), arcsize, arcsize);
			
			Paint itemPaint = renderer.getItemPaint(row, column);
	        GradientPaintTransformer t = renderer.getGradientPaintTransformer();
	        if (t != null && itemPaint instanceof GradientPaint) {
	            itemPaint = t.transform((GradientPaint) itemPaint, bar);
	        }
	        g2.setPaint(itemPaint);    
	        SimulatedAction action = labelGenerator.getAction(row, column);
	        if (action != null) {
	        	Color color = Color.BLACK;
	        	switch(action.getAssociateAction().getFunctor()) {
	        	case AND_DECOMPOSES2:
	        	case AND_DECOMPOSES3:
	        		color = Color.BLUE;
	        		break;
	        	case PASSES:
	        		color = Color.CYAN;
	        		break;
	        	case SATISFIES:
	        		color = Color.GREEN;
	        		break;
	        	}
	        	if (action.getEnd() <= 0)
	        		color = Color.RED;
	        	g2.setColor(color);
	        }
	        g2.fill(bar);
	        
	        Stroke stroke = renderer.getItemOutlineStroke(row, column);
            Paint paint = renderer.getItemOutlinePaint(row, column);
            if (stroke != null && paint != null) {
                g2.setStroke(stroke);
                g2.setPaint(paint);
                g2.draw(bar);
            }
		}
	}
	
	static class MyBarRenderer extends BarRenderer {
		private static final long serialVersionUID = 1L;
		private int highlightRow = -1;
		private int highlightColumn = -1;

		public MyBarRenderer() {
			
		}
		
		public void setHighlightedItem(int row, int column) {
			if ((highlightRow == row) && (highlightColumn == column))
				return;
			highlightRow = row;
			highlightColumn = column;
			notifyListeners(new RendererChangeEvent(this));
		}

		@Override
		public Paint getItemOutlinePaint(int row, int column) {
			if ((row == highlightRow) && (column == highlightColumn))
				return Color.yellow;
			return super.getItemOutlinePaint(row, column);
		}
	}
	
	/**
	 * Create the panel
	 */
	public SimulationTimelinePanel(Composite container) {
		this.container = container;
		
		/*chartPanel.addChartMouseListener(new ChartMouseListener() {

			@Override
			public void chartMouseClicked(ChartMouseEvent arg0) {
				// TODO Auto-generated method stub
				ChartEntity localChartEntity = arg0.getEntity();
			}

			@Override
			public void chartMouseMoved(ChartMouseEvent param) {
				@SuppressWarnings("unused")
				ChartEntity localChartEntity = param.getEntity();
				
				 * if (!(localChartEntity instanceof CategoryItemEntity)) {
				 * renderer.setHighlightedItem(-1, -1); return; }
				 * CategoryItemEntity localCategoryItemEntity =
				 * (CategoryItemEntity) localChartEntity; CategoryDataset
				 * localCategoryDataset = localCategoryItemEntity.getDataset();
				 * renderer.setHighlightedItem(localCategoryDataset.getRowIndex(
				 * localCategoryItemEntity.getRowKey()),
				 * localCategoryDataset.getColumnIndex
				 * (localCategoryItemEntity.getColumnKey()));
				 

			}
		});*/
	}

	public void setSolution(IJasimAppContext context, JasimModel model, PddlSolution solution) {
		SolutionSimulator sim = new SolutionSimulator(context, model, solution);
		sim.simulate(0, 999);
		setSimulator(sim, null);
	}
	
	public void setSimulator(SolutionSimulator simulator, List<HistoryEntry> entries) {
		JFreeChart chart = createChart(simulator, entries != null ? entries : simulator.convertTimeslotToHistory());
		if (chartFrame == null) {
			chartFrame = new ChartComposite(container, SWT.NONE, chart, true);
			chartFrame.setLayoutData(BorderLayout.CENTER);
			
			chartFrame.addChartMouseListener(new ChartMouseListener() {
				
				@Override
				public void chartMouseMoved(ChartMouseEvent event) {
					ChartEntity localChartEntity = event.getEntity();
					if (!(localChartEntity instanceof XYItemEntity)) {
						return;
					}
					XYItemEntity entity = (XYItemEntity) localChartEntity;
					//XYDataset ds = entity.getDataset();
					int series = entity.getSeriesIndex();
					int index = entity.getItem();
					SimulatedAction action = labelGenerator.getAction(series, index);
					String label = action.getAssociateAction().toString() + "\r\n" +
						String.format("dur: %d - %d", action.getStart(), action.getEnd());
					
					entity.setToolTipText(label);
				}
				
				@Override
				public void chartMouseClicked(ChartMouseEvent event) {
					
				}
			});
			container.layout();
			chartFrame.redraw();
		}
		else {
			chartFrame.setChart(chart);
			container.layout();
			chartFrame.forceRedraw();
			//chartFrame.redraw();
		}
		container.redraw();
	}
	
	protected JFreeChart createChart(SolutionSimulator simulator, List<HistoryEntry> entry) {
		
		ArrayList<String> agNames = new ArrayList<String>();
		for (SolutionSimulator.Agent ag : simulator.agents)
			agNames.add(ag.getName());
		
		Collections.sort(agNames);
		labelGenerator = new MyXYItemLabelGenerator();
		
		Dataset ds = createDataset(entry, agNames);
			
		JFreeChart chart = ChartFactory.createXYBarChart("Solution", "Agent", false, "Time", (IntervalXYDataset) ds, PlotOrientation.HORIZONTAL, true, false, false);
		
		XYPlot plot = (XYPlot) chart.getPlot();
		plot.setRangePannable(true);
	
		SymbolAxis localSymbolAxis = new SymbolAxis("Agent", agNames.toArray(new String[] {}));
		localSymbolAxis.setGridBandsVisible(false);
		plot.setDomainAxis(localSymbolAxis);
		
		XYBarRenderer localXYBarRenderer = (XYBarRenderer) plot.getRenderer();
		localXYBarRenderer.setUseYInterval(true);
		localXYBarRenderer.setShadowVisible(false);
		localXYBarRenderer.setBarPainter(new MyBarPainter());
		localXYBarRenderer.setBaseItemLabelsVisible(true);
		localXYBarRenderer.setBaseItemLabelGenerator(labelGenerator);
		
		plot.setRenderer(localXYBarRenderer);
		
		
		NumberAxis axis = (NumberAxis) plot.getRangeAxis();
		TickUnitSource units = NumberAxis.createIntegerTickUnits();
		axis.setStandardTickUnits(units);
		  
		return chart;
		//chartPanel.setChart(chart);
	}

	private Dataset createDataset(List<HistoryEntry> entries, ArrayList<String> agNames) {
		XYIntervalSeriesCollection result = new XYIntervalSeriesCollection();
		
		HashMap<Agent, XYIntervalSeries> series = new HashMap<SolutionSimulator.Agent, XYIntervalSeries>();
		// HashMap<SimulatedAction, Boolean> tasks = new HashMap<SolutionSimulator.SimulatedAction, Boolean>();
		HashMap<Agent, Integer> seriesIndex = new HashMap<Agent, Integer>();
		
		labelGenerator.items.clear();
		
		for (SolutionSimulator.HistoryEntry e: entries) {
			XYIntervalSeries s = series.get(e.action.getAgent());
			if (s == null) {
				series.put(e.action.getAgent(), s = new XYIntervalSeries(e.action.getAgent().getName()));
				seriesIndex.put(e.action.getAgent(), result.getSeriesCount());
				result.addSeries(s);
			}
			int sidx = seriesIndex.get(e.action.getAgent());
			int taskIdx = s.getItemCount();
			int x = agNames.indexOf(e.action.getAgent().getName());
			if (x == -1) {
				x = agNames.size();
				agNames.add(e.action.getAgent().getName());
			}
			s.add(x, x - 0.2f, x + 0.2f, 0, e.start, e.end + 1);
			labelGenerator.items.add(new XYItemLabelItem(sidx, taskIdx, e.action));
		}
		
		return result;
	}
}